// The purpose of this service is to provide an application
// that is using plain go code to define an API
//
//      Host: localhost
//      Version: 0.0.1
//
// swagger:meta
package api

import (
	context2 "context"
	"encoding/json"
	"fmt"
	gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/labstack/echo"
	"github.com/labstack/echo/middleware"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
	"net/http"
	"path"
	"strings"
	"app/model"
	"app/model/mock"
	"app/pb"
	"app/utils"
)

type Server struct {
	log        *logrus.Logger
	httpServer *echo.Echo
	db         model.DB

	webAddr  string
	grpcAddr string

	Mux []gwruntime.ServeMuxOption

	userService pb.UserServiceClient
}

func NewServer(logger *logrus.Logger, webPort int, grpcPort int) (*Server, error) {
	s := new(Server)
	s.log = logger

	s.webAddr = fmt.Sprintf(":%d", webPort)
	s.grpcAddr = fmt.Sprintf(":%d", grpcPort)
	httpServer, err := newEcho(s)

	if err != nil {
		return nil, err
	}

	s.httpServer = httpServer
	s.Mux = []gwruntime.ServeMuxOption{
		// 在上下文中存放用户信息
		gwruntime.WithMetadata(func(i context2.Context, request *http.Request) metadata.MD {
			val, _ := json.Marshal(&model.MockUserValue)
			md := metadata.Pairs("user-info", string(val))
			return md
		}),
	}

	//db, err := mysql.NewDb("mysql://root:root@localhost:3306/demo?charset=utf-8")
	//if err != nil {
	//	return nil,err
	//}
	//

	s.db = mock.NewMockDb()

	return s, nil
}

func newEcho(s *Server) (*echo.Echo, error) {

	e := echo.New()

	conn, err := utils.NewGRpcConn(s.grpcAddr, "", "", "")

	if err != nil {
		return nil, errors.Wrap(err, "can`t connection user service1")
	}

	userService := pb.NewUserServiceClient(conn)
	s.userService = userService

	_, err = s.userService.Ping(context.Background(), &pb.PingRequest{Ping: "ping"})
	if err != nil {
		return nil, errors.Wrap(err, "can`t connection user service2")
	}

	e.Use(middleware.Recover())
	e.Use(func(handlerFunc echo.HandlerFunc) echo.HandlerFunc {
		return func(ctx echo.Context) error {
			ctx.Set(model.UserKey, &model.MockUserValue)
			return handlerFunc(ctx)
		}
	})

	// 注入server 单例.
	e.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
		return func(ctx echo.Context) error {
			ctx.Set("server",s)
			return next(ctx)
		}
	})

	e.Use(middleware.CORS())

	e.GET("/hello", func(i echo.Context) error {
		return i.HTML(200,"测试页面")
	})


	gw, err := utils.NewGateway(context.Background(), conn, s.Mux)

	if err != nil {
		return nil, err
	}

	h := echo.WrapHandler(gw)
	r := e.Group("/api",middleware.Logger())
	{
		r.GET("/ping", h)
		r.POST("/user", h, func(h echo.HandlerFunc) echo.HandlerFunc {
			return func(ctx echo.Context) error {
				fmt.Println("hello world")
				return h(ctx)
			}
		})
	}


	guest := e.Group("")
	{
		sh := echo.WrapHandler(swaggerServer(s.log,"./docs"))
		guest.GET("/api/swagger*",sh)
	}

	return e, nil
}

func (s *Server) Start() error {
	return s.httpServer.Start(s.webAddr)
}


// swaggerServer returns swagger specification files located under "/swagger/"
func swaggerServer(logger logrus.FieldLogger, dir string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {

		if !strings.HasSuffix(r.URL.Path, ".swagger.json") {
			logger.Errorf("Not Found: %s", r.URL.Path)
			http.NotFound(w, r)
			return
		}

		logger.Infof("Serving %s", r.URL.Path)
		p := strings.TrimPrefix(r.URL.Path, "/api/swagger/")
		p = path.Join(dir, p)

		w.Header().Set("Access-Control-Allow-Origin","*")
		w.Header().Set("Access-Control-Allow-Origin","*")
		w.Header().Set("Access-Control-Allow-Headers","*")
		http.ServeFile(w, r, p)
	}
}